<html>

<head>
  <title>Test for selection underline</title>
  <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
  <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />

<script type="text/javascript">

// Canvas related code stolen from layout/base/tests/bidi_numeral_test.js which
// stole from http://developer.mozilla.org/en/docs/Code_snippets:Canvas

var Ci = Components.interfaces;
var Cc = Components.classes;

var RemoteCanvas = function(aIFrame, aTest) {
  this.iframe = aIFrame;
  this.test = aTest;
  this.snapshot = null;
};

RemoteCanvas.CANVAS_WIDTH = 200;
RemoteCanvas.CANVAS_HEIGHT = 100;

RemoteCanvas.prototype.isReference = function() {
  return this.iframe && (this.iframe.id == "reference");
}

RemoteCanvas.prototype.load = function(callback) {
  this.iframe.contentWindow.wrappedJSObject.init(this.test);
  var me = this;
  setTimeout(function () { me.remotePagePrepared(callback) }, 100);
}

RemoteCanvas.prototype.remotePagePrepared = function(callback) {
  this.snapshot = snapshotWindow(this.iframe.contentWindow);
  callback(this);
}

var gPrefs = [
  {
    name: "ui.SpellCheckerUnderline",
    type: "char",
    newValue: "#ff0000"
  },
  {
    name: "ui.IMERawInputBackground",
    type: "char",
    newValue: "transparent"
  },
  {
    name: "ui.IMERawInputForeground",
    type: "char",
    newValue: "#000000"
  },
  {
    name: "ui.IMERawInputUnderline",
    type: "char",
    newValue: "#00ff00"
  },
  {
    name: "ui.IMESelectedRawTextBackground",
    type: "char",
    newValue: "transparent"
  },
  {
    name: "ui.IMESelectedRawTextForeground",
    type: "char",
    newValue: "#000000"
  },
  {
    name: "ui.IMESelectedRawTextUnderline",
    type: "char",
    newValue: "#0000ff"
  },
  {
    name: "ui.IMEConvertedTextBackground",
    type: "char",
    newValue: "transparent"
  },
  {
    name: "ui.IMEConvertedTextForeground",
    type: "char",
    newValue: "#000000"
  },
  {
    name: "ui.IMEConvertedTextUnderline",
    type: "char",
    newValue: "#ffff00"
  },
  {
    name: "ui.IMESelectedConvertedTextBackground",
    type: "char",
    newValue: "transparent"
  },
  {
    name: "ui.IMESelectedConvertedTextForeground",
    type: "char",
    newValue: "#000000"
  },
  {
    name: "ui.IMESelectedConvertedTextUnderline",
    type: "char",
    newValue: "#00ffff"
  },
  {
    name: "ui.SpellCheckerUnderlineStyle",
    type: "int",
    newValue: 0
  },
  {
    name: "ui.IMERawInputUnderlineStyle",
    type: "int",
    newValue: 0
  },
  {
    name: "ui.IMESelectedRawTextUnderlineStyle",
    type: "int",
    newValue: 0
  },
  {
    name: "ui.IMEConvertedTextUnderlineStyle",
    type: "int",
    newValue: 0
  },
  {
    name: "ui.IMESelectedConvertedTextUnderlineStyle",
    type: "int",
    newValue: 0
  },
  {
    name: "ui.SpellCheckerUnderlineRelativeSize",
    type: "float",
    newValue: 1.0
  },
  {
    name: "ui.IMEUnderlineRelativeSize",
    type: "float",
    newValue: 1.0
  }
];

const nsISelectionController = Components.interfaces.nsISelectionController;

var gSelectionIndex = -1;
const kSelections = [
  { type: nsISelectionController.SELECTION_SPELLCHECK,
    typeName: "SpellCheck", isIME: false,
    decorationColor: "#ff0000" },
  { type: nsISelectionController.SELECTION_IME_RAWINPUT,
    typeName: "IME-RawInput", isIME: true,
    decorationColor: "#00ff00" },
  { type: nsISelectionController.SELECTION_IME_SELECTEDRAWTEXT,
    typeName: "IME-SelectedRawText", isIME: true,
    decorationColor: "#0000ff" },
  { type: nsISelectionController.SELECTION_IME_CONVERTEDTEXT,
    typeName: "IME-ConvertedText", isIME: true,
    decorationColor: "#ffff00" },
  { type: nsISelectionController.SELECTION_IME_SELECTEDCONVERTEDTEXT,
    typeName: "IME-SelectedConvertedText", isIME: true,
    decorationColor: "#00ffff" },
];

const kFontName_Ahem = "AhemTest";
const kFontName_MPlus = "mplusTest";

var gFontIndex = 0;
const kFonts = [
  { family: kFontName_Ahem, defaultSize: 16 },
  { family: kFontName_Ahem, defaultSize: 20 },
  { family: kFontName_Ahem, defaultSize: 32 },
  { family: kFontName_Ahem, defaultSize: 52 },

  { family: kFontName_MPlus, defaultSize: 16 },
  { family: kFontName_MPlus, defaultSize: 20 },
  { family: kFontName_MPlus, defaultSize: 32 },
  { family: kFontName_MPlus, defaultSize: 52 },
];

const kDecorationStyleNone   = 0;
const kDecorationStyleDotted = 1;
const kDecorationStyleDashed = 2;
const kDecorationStyleSolid  = 3;
const kDecorationStyleDouble = 4;
const kDecorationStyleWavy   = 5;

var gDecorationIndex = 0;
const kDecorations = [
  { relativeSize: 1.0, style: kDecorationStyleNone,   styleName: "-moz-none" },
  { relativeSize: 1.0, style: kDecorationStyleSolid,  styleName: "solid"     },
  { relativeSize: 1.0, style: kDecorationStyleDotted, styleName: "dotted"    },
  { relativeSize: 1.0, style: kDecorationStyleDashed, styleName: "dashed"    },
  { relativeSize: 1.0, style: kDecorationStyleDouble, styleName: "double"    },
  { relativeSize: 1.0, style: kDecorationStyleWavy,   styleName: "wavy"      },

// XXX relativeSize 2.0 cannot be tested by CSS3 text-decoration

];

function IsD2DEnabled() {
  var enabled = false;

  try {
    enabled = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo).D2DEnabled;
  } catch(e) {}

  return enabled;
}

function getFuzz(test) {
  // Only failing on Windows with Direct2D enabled, and only for 16 permutations.
  if (IsD2DEnabled() &&
      test.decoration.styleName == "solid" &&
      test.decoration.relativeSize == "1" &&
      test.font.family == "mplusTest" &&
      test.selection.typeName != "SpellCheck") {
    return { numDifferentPixels: 194, maxDifference: 1 };
  }
  return null;
}

function run()
{
  if (++gSelectionIndex == kSelections.length) {
    if (++gFontIndex == kFonts.length) {
      if (++gDecorationIndex == kDecorations.length) {
        SimpleTest.finish();
        cleanup();
        return;
      }
      gFontIndex = 0;
    }
    gSelectionIndex = 0;
    SpecialPowers.setIntPref("font.size.variable.x-western",
                             kFonts[gFontIndex].defaultSize);
  }

  var test = {
    font: kFonts[gFontIndex],
    decoration: kDecorations[gDecorationIndex],
    selection: kSelections[gSelectionIndex],
  };

  SpecialPowers.setIntPref("ui.SpellCheckerUnderlineRelativeSize",
                           test.decoration.relativeSize * 100);
  SpecialPowers.setIntPref("ui.IMEUnderlineRelativeSize",
                           test.decoration.relativeSize * 100);
  SpecialPowers.setIntPref("ui.SpellCheckerUnderlineStyle",
                           test.decoration.style);
  SpecialPowers.setIntPref("ui.IMERawInputUnderlineStyle",
                           test.decoration.style);
  SpecialPowers.setIntPref("ui.IMESelectedRawTextUnderlineStyle",
                           test.decoration.style);
  SpecialPowers.setIntPref("ui.IMEConvertedTextUnderlineStyle",
                           test.decoration.style);
  SpecialPowers.setIntPref("ui.IMESelectedConvertedTextUnderlineStyle",
                           test.decoration.style);

  SimpleTest.executeSoon(function () { doTest(test); });
}

function doTest(aTest)
{

  var canvases = [];
  function callbackTestCanvas(canvas)
  {
    canvases.push(canvas);

    if (canvases.length != 2)
      return;

    var result = !canvases[0].isReference() ? canvases[0] : canvases[1];
    var reference = canvases[0].isReference() ? canvases[0] : canvases[1];

    var description = "(selection: " + aTest.selection.typeName +
                      ", style: " + aTest.decoration.styleName +
                      ", relativeSize: " + aTest.decoration.relativeSize +
                      ", font: " + aTest.font.family +
                      ", default font size: " + aTest.font.defaultSize + ")";

    // If the decoration line is thick and the descender of the text isn't
    // enough for containing it, selection underline may be painted lower
    // if it's possible.  Then, we cannot test it with CSS3 text-decoration.
    if (aTest.decoration.style == kDecorationStyleDouble ||
        aTest.decoration.style == kDecorationStyleWavy) {
      todo(false, "Rendering of" + description);
    } else {
      assertSnapshots(result.snapshot, reference.snapshot, true,
                      getFuzz(aTest), description, "");
    }

    canvases = [];

    run();
  }

  var testCanvas = new RemoteCanvas(document.getElementById("result"), aTest);
  testCanvas.load(callbackTestCanvas);

  var refCanvas = new RemoteCanvas(document.getElementById("reference"), aTest);
  refCanvas.load(callbackTestCanvas);
}

function onLoad()
{
  for (var i = 0; i < gPrefs.length; i++) {
    if (gPrefs[i].type == "char") {
      SpecialPowers.setCharPref(gPrefs[i].name, gPrefs[i].newValue);
    } else if (gPrefs[i].type == "int") {
      SpecialPowers.setIntPref(gPrefs[i].name, gPrefs[i].newValue);
    } else if (gPrefs[i].type == "float") {
      SpecialPowers.setIntPref(gPrefs[i].name, gPrefs[i].newValue * 100);
    }
  }

  var iframe = document.getElementById("result");
  iframe.width = RemoteCanvas.CANVAS_WIDTH + "px";
  iframe.height = RemoteCanvas.CANVAS_HEIGHT + "px";
  iframe = document.getElementById("reference");
  iframe.width = RemoteCanvas.CANVAS_WIDTH + "px";
  iframe.height = RemoteCanvas.CANVAS_HEIGHT + "px";

  run();
}

function cleanup()
{
  SpecialPowers.clearUserPref("font.size.variable.x-western");
  for (var i = 0; i < gPrefs.length; i++) {
    SpecialPowers.clearUserPref(gPrefs[i].name);
  }
}

SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(onLoad, window);

</script>

</head>
<body>

<iframe src="frame_selection_underline.xhtml" id="result"></iframe>
<iframe src="frame_selection_underline-ref.xhtml" id="reference"></iframe>
<pre id="test">
</pre>

</body>
</html>
